﻿using Lightworks.Core.Cache;
using Lightworks.Core.Repository;
using Lightworks.Core.Service;
using Lightworks.Core.Utils;
using Lightworks.System.Dto;
using Lightworks.System.Models;
using Mapster;
using MapsterMapper;
using Microsoft.Extensions.Logging;
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lightworks.System.Services.Impl
{
    [Service]
    public class UserService : IUserService
    {
        SugarRepository<User> repository;
        ILogger<UserService> logger;
        Core.Cache.ICacheService cacheService;
        public UserService(SugarRepository<User> repo, ILogger<UserService> logger, Core.Cache.ICacheService cacheService) 
        {
            repository = repo;
            this.logger = logger;
            this.cacheService = cacheService;
        }

        public long Create(AddUserReq req)
        {
            User user = req.Adapt<User>();
            user.DeptId = req.DeptPath==null ? 0 : req.DeptPath[req.DeptPath.Length-1];
            user.Salt = new Random().Next(10000000, 99999999).ToString();
            user.Password = HashHelper.HashPwd(user.Password!, Encoding.UTF8.GetBytes(user.Salt));

            using var ctx = repository.Context.CreateContext();
            long id = ctx.Db.Insertable(user).ExecuteReturnBigIdentity();
            List<UserRole> ur = new List<UserRole>();
            if (req.RoleIds != null)
            {
                foreach (var item in req.RoleIds)
                {
                    ur.Add(new UserRole { UserId = id, RoleId = item });
                }
                ctx.Db.Deleteable<UserRole>().Where(u => u.UserId == user.Id);
                ctx.Db.Insertable(ur).ExecuteCommand();
            }
            ctx.Commit();
            if (ctx.IsCommit)
                return id;
            return 0;
        }

        public PageData<UserRes> GetUserList(UserSearchReq req)
        {
            PageData<UserRes> result = new PageData<UserRes>();
            var predicate = Expressionable.Create<User>()
                .AndIF(!string.IsNullOrWhiteSpace(req.Keywords),
                u => u.UserName!.Contains(req.Keywords!)
                || u.Name!.Contains(req.Keywords!))
                .AndIF(req.DeptId != 0, u=> SqlFunc.JsonArrayAny(u.Dept!.DeptPath, req.DeptId));
            int count = 0;
            var query = repository.Context.Queryable<User>()
                .Includes(x=>x.Roles)
                .Includes(x=>x.Dept)
                .Where(predicate.ToExpression())
                .OrderBy(req.GetOrderModel())
                .ToPageList(req.Page, req.PageSize, ref count);
            result.Total = count;
            result.Rows = query.Select(x => x.Adapt(new UserRes
            {
                DeptPath = x.Dept == null ? [] : x.Dept.DeptPath,
                RoleIds = x.Roles == null ? [] : x.Roles.Select(r=>r.Id).ToArray(),
                RoleNames = string.Join(",", x.Roles == null ? [] : x.Roles.Select(r => r.Alias).ToArray())
            })).ToList();
            return result;
        }

        public bool ResetPwd(string userName, string oldPwd, string newPwd)
        {
            var user = repository.GetFirst(u=>u.UserName == userName);
            if(user.Password != HashHelper.HashPwd(oldPwd, Encoding.UTF8.GetBytes(user.Salt!))) 
            {
                return false;
            }
            user.Password = HashHelper.HashPwd(oldPwd, Encoding.UTF8.GetBytes(user.Salt));
            return repository.Update(user);
        }

        public bool Delete(long[] ids)
        {
            using (var ctx = repository.Context.CreateContext()) 
            {
                try
                {
                    ctx.Db.Deleteable<User>().Where(u => ids.Contains(u.Id)).ExecuteCommand();
                    ctx.Db.Deleteable<UserRole>().Where(ur => ids.Contains(ur.UserId)).ExecuteCommand();
                    ctx.Commit();
                    return true;
                }catch (Exception ex)
                {
                    logger.LogError(ex.Message, ex);
                    return false;
                }
            }
        }

        public bool Save(UpdateUserReq req)
        {
            var deptId = req.DeptPath == null ? 0 : req.DeptPath[req.DeptPath.Length - 1];
            using (var ctx = repository.Context.CreateContext())
            {
                try
                {
                    ctx.Db.Updateable<User>().SetColumns(it => new User { 
                        UserName = req.UserName,
                        Name = req.UserName,
                        DeptId = deptId,
                        Status = req.Status
                    }).Where(it=>it.Id == req.Id).ExecuteCommand();
                    List<UserRole> ur = new List<UserRole>();
                    if (req.RoleIds != null)
                    {
                        foreach (var item in req.RoleIds)
                        {
                            ur.Add(new UserRole { UserId = req.Id, RoleId = item });
                        }
                        ctx.Db.Deleteable<UserRole>().Where(u => u.UserId == req.Id).ExecuteCommand();
                        ctx.Db.Insertable(ur).ExecuteCommand();
                    }
                    ctx.Commit();
                    return true;
                }
                catch (Exception ex) 
                {
                    logger.LogError(ex.Message, ex);
                    return false;
                }
            }
        }

        public bool SetAvatar(string avatar)
        {
            throw new NotImplementedException();
        }

        public User GetUser(string username) 
        {
            var user = cacheService.Get<User>($"system:user:detial:{username}");
            if (user == null) 
            {
                user = repository.GetFirst(u=>u.UserName == username);
                cacheService.Set($"system:user:detial:{username}", user);
            }
            return user;
        }
    }
}
